package com.ybear.ybmediax.audio;

import android.content.res.AssetFileDescriptor;
import android.os.Build;

import androidx.annotation.IntRange;
import androidx.annotation.NonNull;

import com.ybear.ybutils.utils.StreamUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 音频播放器
 *
 * 如果调用的是Asset文件夹中的资源，
 * 并且报了这个错误：java.io.FileNotFoundException: This file can not be opened as a file descriptor; it is probably compressed
 * 你需要在build.gradle中添加 "aaptOptions { noCompress "audio" }"，其中pcm是取消优化的文件格式
 * 例如：
 * android {
 *     defaultConfig {...}
 *
 *     //取消对pcm格式的优化
 *     aaptOptions {
 *         noCompress "audio"
 *     }
 * }
 */
public class AudioX {
    private android.media.AudioTrack mAudioTrack;
    private Config mConfig;
    private OnAudioStatusListener mOnAudioStatusListener;

    private float mVolume = 1.0F;
    private int mLoopStartInFrames;
    private int mLoopEndInFrames;
    private int mLoopCount = -1;
    private volatile int mStatus = 0;
    private final ExecutorService mPool = Executors.newFixedThreadPool( 1 );
    private ByteArrayOutputStream mAudioOutputStream;

    /**
     * 播放的文件
     * @param fis           pcm音频文件流
     * @return              this
     */
    public AudioX setData(FileInputStream fis) {
        if( fis == null ) return this;
        mPool.execute(() -> {
            mAudioOutputStream = new ByteArrayOutputStream();
            StreamUtils.write(fis, mConfig.getMinBufferSize(), ( bs, len ) -> {
                if( bs == null || len == -1 ) return;
                mAudioOutputStream.write( bs, 0, len );
            });
        });
        return this;
    }

    /**
     * 播放的文件
     * @param afd           pcm音频文件
     * @return              this
     */
    public AudioX setData(AssetFileDescriptor afd) {
        FileInputStream fis = null;
        try {
            fis = afd.createInputStream();
        } catch (IOException e) {
            errorListener( e );
        }
        return setData( fis );
    }

    /**
     * 播放的文件
     * @param f             pcm音频文件
     * @return              this
     */
    public AudioX setData(File f) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream( f );
        } catch (FileNotFoundException e) {
            errorListener( e );
        }
        return setData( fis );
    }

    /**
     * 播放的文件路径
     * @param path          pcm音频文件路径
     * @return              this
     */
    public AudioX setData(String path) { return setData( new File( path ) ); }

    /**
     * pcm音频播放配置
     * @param config        配置项
     * @return              this
     */
    public AudioX setConfig(@NonNull Config config) {
        mConfig = config;
        return this;
    }

    /**
     * 循环的位置
     * @param startInFrames     开始帧
     * @param endInFrames       结束帧
     * @param loopCount         循环次数
     * @return                  this
     */
    public AudioX setLoopPoints(@IntRange(from = 0) int startInFrames,
                                @IntRange (from = 0) int endInFrames,
                                @IntRange (from = -1) int loopCount) {
        mLoopStartInFrames = startInFrames;
        mLoopEndInFrames = endInFrames;
        mLoopCount = loopCount;
        return this;
    }

    /**
     * 设置音量大小
     * @param gain      音量 0.0 ~ 1.0。默认：1.0
     * @return          this
     */
    public AudioX setVolume(float gain) {
        mVolume = gain;
        return this;
    }

    public void updateVolume(float gain) {
        if( mAudioTrack == null ) return;
        if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ) {
            mAudioTrack.setVolume( gain );
        }else {
            mAudioTrack.setStereoVolume( gain, gain );
        }
    }

    /**
     * 播放音频
     * 重复调用时只执行一次
     */
    public void play() {
        if( isPlaying() ) return;
        if( mConfig == null ) mConfig = Config.newConfig();
        int bufferSize = mConfig.getMinBufferSize();
        if( mAudioTrack == null ) {
            mAudioTrack = new android.media.AudioTrack(
                    mConfig.getStreamType(),
                    mConfig.getSampleRateInHz(),
                    mConfig.getChannelConfig(),
                    mConfig.getEncoding(),
                    bufferSize,
                    android.media.AudioTrack.MODE_STREAM
            );
        }
        //音量
        updateVolume( mVolume );
        //循环位置
        if( mLoopCount != -1 ) {
            mAudioTrack.setLoopPoints( mLoopStartInFrames, mLoopEndInFrames, mLoopCount );
        }
        mPool.execute(() -> {
            try {
                playStream( bufferSize );
            } catch (IllegalStateException e) {
                errorListener( e );
            }
        });
    }

    /**
     * 播放音频流
     * @param bufferSize        流大小
     */
    private void playStream(int bufferSize) throws IllegalStateException {
        if( mAudioTrack == null || mAudioOutputStream == null ) {
            errorListener( new NullPointerException("AudioX: There is no stream to play.") );
            return;
        }
        byte[] data = new byte[ bufferSize ];
        ByteArrayInputStream is = new ByteArrayInputStream( mAudioOutputStream.toByteArray() );

        mStatus = 1;
        mAudioTrack.play();
        playListener();
        while ( mStatus == 1 ) {
            //装载流数据
            int index = is.read( data, 0, data.length );
            //写入音频数据
            if( index == -1 ) break;
            mAudioTrack.write( data, 0, index );
        }
        //播放完成
        stop();
        completedListener();

        try {
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 暂停音频
     * 重复调用时只执行一次
     */
    public void pause() {
        if( mStatus == 2 ) return;
        mStatus = 2;
        try {
            if( mAudioTrack != null ) mAudioTrack.pause();
        } catch (Exception e) {
            errorListener( e );
        }
        pausedListener();
    }

    /**
     * 停止音频
     * 重复调用时只执行一次
     */
    public void stop() {
        if( mStatus == 3 ) return;
        mStatus = 3;
        try {
            if( mAudioTrack != null ) mAudioTrack.stop();
        } catch (Exception e) {
            errorListener( e );
        }
    }

    public void flush() { if( mAudioTrack != null ) mAudioTrack.flush(); }

    /**
     * 是否在播放音频
     * @return          是否在播放
     */
    public  boolean isPlaying() { return mStatus == 1; }

    /**
     * 释放资源
     */
    public void release() {
        try {
            if( mAudioTrack != null ) mAudioTrack.release();
        } catch (Exception e) {
            errorListener( e );
        } finally {
            mAudioTrack = null;
        }
        try {
            if( mAudioOutputStream != null ) mAudioOutputStream.close();
        } catch (Exception e) {
            errorListener( e );
        } finally {
            mAudioOutputStream = null;
        }
    }

    /**
     * 设置音频状态事件监听器
     * @param l         监听器
     */
    public void setOnAudioStatusListener(OnAudioStatusListener l) { mOnAudioStatusListener = l; }

    private void playListener() {
        if( mOnAudioStatusListener == null ) return;
        mOnAudioStatusListener.onPlayed();
    }
    private void pausedListener() {
        if( mOnAudioStatusListener == null ) return;
        mOnAudioStatusListener.onPaused();
    }
    private void completedListener() {
        if( mOnAudioStatusListener == null ) return;
        mOnAudioStatusListener.onCompleted();
    }

    private void errorListener(@NonNull Exception e) {
        if( mOnAudioStatusListener == null ) {
            printStackTrace( e );
            return;
        }
        if( mOnAudioStatusListener.onError( e ) ) {
            printStackTrace( e );
        }
    }
    private void printStackTrace(@NonNull Exception e) { e.printStackTrace(); }
}